/*********************************
 * jsmud: JavaScript MUD Kernel  *
 *          Mojodo, Inc.         *
 *        Sean Middleditch       *
 * Copyright (C) 2005,2007,2008  *
 *      All rights reserved.     *
 *   See COPYING for licensing.  *
 *********************************/

/* ==== SYSTEM INCLUDES ==== */

#include <string.h>
#include <errno.h>
#include <limits.h>
#include <arpa/telnet.h>
#include <sys/stat.h>

/* ==== LIBRARY INCLUDES ==== */

#include <sqlite3.h>
#include <zlib.h>

#define XP_UNIX 1
#define MOZILLA_1_8_BRANCH 1
#include <jsapi.h>
#undef XP_UNIX
#undef MOZILLA_1_8_BRANCH

/* ==== JSMUD HEADER ==== */

#include "jsmud.h"

/* ==== DECLARATIONS ==== */

JSAPI(js_net_listen);
JSAPI(js_net_registerzmp);
JSAPI(js_net_haszmp);

JSAPI(js_connection_send);
JSAPI(js_connection_sendzmp);
JSAPI(js_connection_setecho);
JSAPI(js_connection_disconnect);
static void js_connection_finalize (JSContext* cx, JSObject* self);

/* ==== SCRIPT BINDINGS - Net ==== */

static JSFunctionSpec js_net_functions[] = {
	{"listen", js_net_listen, 3, 0, 0},
	{"registerZMP", js_net_registerzmp, 2, 0, 0},
	{"hasZMP", js_net_haszmp, 1, 0, 0},
	{NULL, 0, 0, 0, 0}
};

JSClass js_connection_class = {
	"Connection", JSCLASS_HAS_PRIVATE,
	JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,
	JS_EnumerateStub,JS_ResolveStub,JS_ConvertStub,js_connection_finalize,
	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
};
JSObject* js_connection_prototype;

static JSFunctionSpec js_connection_methods[] = {
	{"send", js_connection_send, 1, 0, 0},
	{"sendZMP", js_connection_sendzmp, 1, 0, 0},
	{"setEcho", js_connection_setecho, 1, 0, 0},
	{"disconnect", js_connection_disconnect, 0, 0, 0},
	{NULL, 0, 0, 0, 0}
};

JSAPI(js_net_listen)
{
	JSString* string;
	const char* interface;
	int32 port;

	if ((string = JS_ValueToString(cx, argv[0])) == NULL)
		return JS_FALSE;
	interface = JS_GetStringBytes(string);

	if (!JS_ValueToInt32(cx, argv[1], &port))
		return JS_FALSE;

	if (JS_ValueToFunction(cx, argv[2]) == NULL)
		return JS_FALSE;

	if (listen_new(interface, port, &argv[2]) == NULL)
		return JS_FALSE;

	return JS_TRUE;
}

JSAPI(js_net_registerzmp)
{
	JSString* string;

	if ((string = JS_ValueToString(cx, argv[0])) == NULL)
		return JS_FALSE;

	if (JS_ValueToFunction(cx, argv[1]) == NULL)
		return JS_FALSE;

	if (zmp_register(JS_GetStringBytes(string), &argv[1]) == NULL)
		return JS_FALSE;

	return JS_TRUE;
}

JSAPI(js_net_haszmp)
{
	JSString* string;

	if ((string = JS_ValueToString(cx, argv[0])) == NULL)
		return JS_FALSE;

	if (zmp_has(JS_GetStringBytes(string)))
		*rval = BOOLEAN_TO_JSVAL(JS_TRUE);
	else
		*rval = BOOLEAN_TO_JSVAL(JS_FALSE);

	return JS_TRUE;
}

JSAPI(js_connection_send)
{
	Client* client;
	char* text;

	if (!JS_ConvertArguments(cx, argc, argv, "s", &text))
		return JS_FALSE;

	if ((client = JS_GetInstancePrivate(cx, self, &js_connection_class, NULL)) == NULL)
		return JS_FALSE;

	client_send_text(client, text, strlen(text));
	
	return JS_TRUE;
}

JSAPI(js_connection_sendzmp)
{
	Client* client;
	int i;
	const char* args[TELNET_ZMP_MAX_ARGS];

	if (!JS_ConvertArguments(cx, argc, argv, "s", &args[0]))
		return JS_FALSE;

	if (argc > TELNET_ZMP_MAX_ARGS) {
		JS_ReportError(cx, "sendZMP only supports %d arguments", TELNET_ZMP_MAX_ARGS);
		return JS_FALSE;
	}

	if ((client = JS_GetInstancePrivate(cx, self, &js_connection_class, NULL)) == NULL)
		return JS_FALSE;

	for (i = 1; i < argc; ++i) {
		if (!(argv[i] = STRING_TO_JSVAL(JS_ValueToString(cx, argv[i]))))
			return JS_FALSE;
	}

	client_send_zmp(client, args[0], i-1, &args[1]);
	
	return JS_TRUE;
}

JSAPI(js_connection_setecho)
{
	Client* client;
	JSBool value;

	if ((client = JS_GetInstancePrivate(cx, self, &js_connection_class, NULL)) == NULL)
		return JS_FALSE;

	if (JS_ValueToBoolean(cx, argv[0], &value) == JS_FALSE)
		return JS_FALSE;

	if (value) {
		client->flags |= TF_WANT_ECHO;
		client_send_iac (client, WONT, TELOPT_ECHO);
	} else {
		client->flags &= ~TF_WANT_ECHO;
		client_send_iac (client, WILL, TELOPT_ECHO);
	}

	return JS_TRUE;
}

JSAPI(js_connection_disconnect)
{
	Client* client;

	if ((client = JS_GetInstancePrivate(cx, self, &js_connection_class, NULL)) == NULL)
		return JS_FALSE;

	client_disconnect(client);

	return JS_TRUE;
}

static void js_connection_finalize (JSContext* cx, JSObject* self)
{
	Client* client;
	if ((client = JS_GetInstancePrivate(cx, self, &js_connection_class, NULL)) != NULL)
		client_disconnect(client);
}

/* ==== INITIALIZE BINDINGS ==== */

int init_netlib (void)
{
	JSObject* ns_net;

	/* create "namespace" object */
	if ((ns_net = JS_DefineObject(js_context, js_global, "Net", NULL, NULL, JSPROP_ENUMERATE|JSPROP_PERMANENT)) == NULL) {
		log_error("JS_DefineObject() failed");
		return 0;
	}

	/* create all our custom functions */
	if (!JS_DefineFunctions(js_context, ns_net, js_net_functions)) {
		log_error("JS_DefineFunctions() failed");
		return 0;
	}

	/* create our telnet client class */
	if ((js_connection_prototype = JS_InitClass(js_context, ns_net, NULL, &js_connection_class, NULL, 0, NULL, js_connection_methods, NULL, NULL)) == NULL) {
		log_error("JS_InitClass() failed");
		return 0;
	}
	JS_AddRoot(js_context, &js_connection_prototype);

	return 1;
}

/* vim: set tabstop=2 shiftwidth=2 ai: */
